const DAO = require("../dao/DAO");
const db = require("../models");
const config = require("../config");
const utils = require("../utils");
const logger = require('../utils/_logger');
const jwt = require('jsonwebtoken');
const Sse = require('./sys_sse.controller')
const Users = db.models.users;
const LoginLogs = db.models.loginLogs;

const behavior_key = (pm) => {
    let keys = Object.keys(pm);
    if (keys.includes("user_name")) return `账号登录->${pm.user_name}`;
    if (keys.includes("email")) return `邮箱登录->${pm.email}`;
    if (keys.includes("phone")) return `手机号登录->${pm.phone}`;
    return `未知登录方式->${JSON.stringify(pm)}`;
}

/**
 * 生成 accessToken 请求令牌 3h
 * @param {data} user 
 * @param {string} sessionId 浏览器指纹
 * @param {string} connectSid 服务器sessionId
 * @returns {string} accessToken
 */
function createAccessToken(user, sessionId, connectSid) {
    return jwt.sign({
        sessionId,
        connectSid,
        id: user.id,
        nick_name: user.nick_name,
        user_name: user.user_name,
        roles: user.pure_roles,
        role_ids: user.pure_roles.map(i => i.id),
    }, config.jwtSecretKey, { expiresIn: '3h' });
}

/**
 * 生成 refreshToken
 * @param {*} user 用户信息
 * @param {*} sessionId sessionId 浏览器指纹
 * @param {*} time refreshToken 有效期
 * @returns refreshToken
 */
function createRefreshToken({ data, sessionId, connectSid, time }) {
    const obj = {
        sessionId,
        connectSid,
        user: { id: data.id },
    }
    db.redis.setex(`${connectSid}:${sessionId}:token`, time, JSON.stringify(obj));
}

// 统一返回
function resExtra(data, { pm, req, res, log }) {
    const roles = data.pure_roles.map(i => i.code);
    if (!roles.length) return res.sendResultAto(null, -1, "该账号没有权限，请联系管理员！");
    const connectSid = pm.connectSid ?? req.cookies["connect.sid"];
    // refreshToken 和 sessionId 
    const sessionId = req.headers["visitor-id"];
    // 生成 accessToken
    const access_token = createAccessToken(data, sessionId, connectSid);
    // 过期时间 默认 3h
    let time = 3 * 3600;
    if (pm.no_login) { // 免登录
        time = pm.login_day * 24 * 3600;
    }
    if (log ?? true) {
        createRefreshToken({ data, sessionId, connectSid, time })
        utils.ip(req).then(ip => {
            DAO.create(LoginLogs, { ...ip, user_id: data.id, behavior: behavior_key(pm) })
            Sse.sendByLogin(data.id, sessionId, () => {
                db.sseClient[data.id] = { sessionId, userId: data.id, info: ip, connectSid, clientList: [] };
            });
        })
    }
    const expires = + new Date() + (1000 * 60 * 60 * 3) - 300000; // 3小时 提前 5 分钟过期
    return {
        roles,
        id: data.id,
        connectSid,
        avatar: data.avatar,
        user_name: data.user_name,
        nick_name: data.nick_name,
        access_token,
        loginDay: pm.login_day,
        refresh_token: sessionId,
        expires
    }
}

// 公共登录方法
function findUser(where, { pm, req, res, log }) {
    Users.findOne({
        where,
        include: [{
            model: db.models.roles,
            attributes: ['id', 'name', 'code'],
            through: { attributes: [] }
        }]
    }).then(data => {
        data = data?.toJSON();
        if (!data) {
            logger.infoLog(`登录失败没有用户====>body:${JSON.stringify(where)}`)
            utils.ip(req).then(ip => {
                DAO.create(LoginLogs, { ...ip, user_id: data.id, status: false, behavior: behavior_key(pm) })
            })
            res.sendResultAto(null, log ? -1 : 4, log ? "账号或密码错误！" : "请登录");
            return
        }
        res.sendResultAto(resExtra(data, { pm, req, res, log }), 0, log ? "登录成功" : "操作成功！");
    })
}
exports.login = ({ pm, req, res }) => {
    Users.findOne({
        // 用户名区分大小写
        where: db.sequelize.where(db.sequelize.fn('BINARY', db.sequelize.col('user_name')), {
            [db.Op.eq]: db.sequelize.fn('BINARY', pm.user_name),
        }),
        include: [{
            model: db.models.roles,
            attributes: ['id', 'name', 'code'],
            through: { attributes: [] }
        }]
    }).then(data => {
        data = data?.toJSON() ?? {};
        delete req.session.captcha_text;
        delete req.session.captcha_key;
        if (data.id && pm.password === data.password && data.status) {
            res.sendResultAto(resExtra(data, { pm, req, res }), 0, "登录成功");
        } else {
            utils.ip(req).then(ip => {
                DAO.create(LoginLogs, { ...ip, user_id: data?.id, status: false, behavior: behavior_key(pm) })
            })
            if (!data?.status) return res.sendResultAto(null, -1, "该账号已被禁用，请联系管理员！")
            logger.infoLog(`账号或密码错误====>name:${pm.user_name}`)
            res.sendResultAto(null, -1, "账号或密码错误！")
        }
    }).catch(err => {
        logger.errLog(err)
        res.sendResultAto(null, -1, "账号或密码错误！")
    })
}

// 刷新 token
exports.refreshToken = async (req, res) => {
    const sessionId = req.headers["visitor-id"];
    if (!req.body.id || !sessionId) return res.sendResultAto(null, 4, "请登录");
    db.redis.get(`${req.body.id}:${sessionId}:token`, (err, obj) => {
        if (err) {
            logger.errLog(err);
            return res.sendResultAto(null, 4, "请登录");
        }
        obj = JSON.parse(obj);
        if (!obj) return res.sendResultAto(null, 4, "请登录");
        findUser(obj.user, { pm: { connectSid: req.body.id }, req, res, log: false })
    })
}
exports.findUser = findUser;